﻿/*
* Detergent CascadingDropDown : a jQuery plugin, version: 1.0 (2012-02-10)
* @requires jQuery v1.7.1 or later

* By Ross Dickinson: http://www.rossisdead.com
* Based off the CascadingDropDown plugin by Raj Kaimal: https://github.com/rajkaimal/CascadingDropDown
* Licensed under the MIT: http://www.opensource.org/licenses/mit-license.php
*
* Detergent is a jQuery plugin that can be attached to a select list to get automatic population. 
* Each time the source element changes value, an ajax request is made to retrieve a list of values for 
* the select list. The respose from the ajax request should be in json with the following format
*
*    [{
*        "text": "John",
*        "value": "10326"
*    },
*    {
*        "text": "Jane",
*        "value": "10801"
*    }] 
*/

// TODO: Finish the destroy method. Needed on both normal and unobtrusive.
// TODO: Figure out how this is gonna work with being initialized from ajax content.
//       Don't wanna have the same annoyance that the unobtrusive validation library has.

(function($) {

    var detergent = function(options) {
        if (typeof options.parentSelector === undefined) {
            throw 'A parent selector is required';
        }
        if (typeof options.actionPath === undefined) {
            throw 'An action path is requried';
        }
        options = $.extend({}, methods.defaults, options);
        return this.each(function() { methods.initialize($(this), options); });
    };

    var methods = {
        'defaults': {
            // Values that must be set
            'actionPath': null,
            'parentSelector': null,

            // Optional
            'async': true,
            'promptText': '-- Select --',
            'loadingText': 'Loading ..',
            'errorText': 'Error loading data.',
            'unselectedText': '-- Select --',
            'postData': null,
            'onLoading': null,
            'onLoaded': null,
            'httpMethod': 'GET'
        },

        'addOption': function($select, value, text) {
            $select.append(methods.createOption(value, text));
        },
        'clear': function($select) {
            $select.empty();
        },
        'createOption': function(value, text) {
            value = value || '';
            return $('<option></option>')
                    .attr('value', value)
                    .text(text || value);
        },
        'destroy': function($select) {
            var options = methods.getOptions($select);
            $(options.parentSelector).unbind('.detergent');
            $select.removeData('options', null);
            $select.removeData('data-cascading-initialized');
        },
        'disable': function($select) {
            methods.clear($select);
            $select.attr('disabled', 'disabled');
            methods.addOption($select, '', methods.getOptions($select).promptText);
            $select.trigger('change');
        },
        'enable': function($select) {
            $select.removeAttr('disabled');
            $select.trigger('change');
        },
        'getOptions': function($select) {
            return $select.data('options');
        },
        'initialize': function($select, options) {
            var initialized = !(!$select.data('data-cascading-initialized'));
            if (!initialized) {
                methods.setOptions($select, options);
                if ($select.children().size() === 0) {
                    methods.disable($select);
                }
                $(options.parentSelector).bind('change.detergent', function() {
                    if ($(options.parentSelector).val()) {
                        methods.post($select);
                    }
                    else {
                        methods.disable($select);
                    }
                });
                $select.data('data-cascading-initialized', true);
            }
            else {
                if (options === 'destroy') {
                    methods.destroy($select);
                }
            }
        },
        'onLoaded': function($select) {
            methods.enable($select);
            var onload = methods.getOptions($select).onLoaded;
            $.isFunction(onload) && onload.call($select);
        },
        'onLoading': function($select) {
            methods.clear($select);
            var opts = methods.getOptions($select);
            methods.addOption($select, '', opts.loadingText);
            $.isFunction(opts.onLoading) && opts.onLoading.call($select);
        },
        'onAjaxSuccess': function($select, data) {
            var $data = $(data);
            if ($data.length === 0) {
                methods.disable($select);
            }
            else {
                methods.reset($select);
                $data.each(function() {
                    methods.addOption($select, this.value || this.Value, this.text || this.Text);
                });
                methods.onLoaded($select);
            }
        },
        'post': function($select) {
            methods.onLoading($select);
            var opts = methods.getOptions($select);
            $.ajax({
                'async': opts.async,
                'data': ((typeof opts.postData == 'function') ? opts.postData() : opts.postData) || $(opts.parentSelector).serialize(),
                'dataType': 'json',
                'error': function() {
                    methods.showError($select);
                },
                'success': function(data) {
                    return methods.onAjaxSuccess($select, data);
                },
                'type': opts.httpMethod,
                'url': opts.actionPath
            });
        },
        'reset': function($select) {
            methods.clear($select);
            $select.trigger('change');
        },
        'setOptions': function($select, value) {
            return $select.data('options', value);
        },
        'showError': function($select) {
            methods.clear($select);
            methods.addOption($select, '', methods.getOptions($select).errorText);
        }
    };

    // This makes our methods easily replacable.
    $.extend(detergent, methods);
    // And finally, make it accessible to jQuery
    $.fn.detergent = detergent;

})(jQuery);


(function($) {

    var unobtrusiveDetergent = function(options) {
        this.each(function() {
            methods.initialize($(this), options);
        });
    };

    var methods = {
        'initialize': function($dropDown, options) {
            // This makes it easier for us to pass in any group of
            // select elements without having to include the data-cascading
            // attribute check outside of this method.
            if ($dropDown.attr('data-cascading') !== 'true') {
                // No attribute = no init.
                return;
            }
            // meaning someone just called $('something').unobtrusiveDetergent();
            if (!options) {
                var data = methods.getSettings($dropDown);
                // Pass in the data object since it'll count as the various text options
                $dropDown.detergent(data);
            }
            else {
                // The only option to pass at the moment is 'destroy'.
                methods.destroy($dropDown);
            }
        },
        'destroy': function($dropDown) {
            $dropDown.detergent('destroy');
        },
        'getSettings': function($dropDown) {
            var data = $dropDown.data('cascadingData');
            if (data == null) {
                data = {
                    'actionPath': $dropDown.attr('data-cascading-action'),
                    'parentSelector': $dropDown.attr('data-cascading-dependson'),
                    'errorText': $dropDown.attr('data-cascading-errortext'),
                    'httpMethod': $dropDown.attr('data-cascading-httpmethod'),
                    'loadingText': $dropDown.attr('data-cascading-loadingtext'),
                    'promptText': $dropDown.attr('data-cascading-prompttext'),
                    'async': methods.getAsync($dropDown)
                };
                $dropDown.data('cascadingData', data);
            }
            return data;
        },
        'getAsync': function($dropDown) {
            var val = $dropDown.attr('data-cascading-async');
            if (val === undefined) {
                // It's a string, and if it's not set, we're defaulting to true.
                return true;
            }
            else {
                return (val === 'true');
            }
        }
    };

    $.fn.unobtrusiveDetergent = unobtrusiveDetergent;
})(jQuery);

$(document).ready(function() {
    $('select').unobtrusiveDetergent();
});